package com.xros.demo.fileserver.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xros.demo.fileserver.domain.entity.FileEntity;
import com.xros.demo.fileserver.domain.vo.ResultVo;
import com.xros.demo.fileserver.mapper.FileMagicNumMapper;
import com.xros.demo.fileserver.mapper.FileMapper;
import com.xros.demo.fileserver.service.IFileService;
import com.xros.demo.fileserver.type.FileType;
import com.xros.demo.fileserver.utils.FileUtils;
import com.xros.demo.fileserver.utils.Log4jUtils;
import com.xros.demo.fileserver.utils.MagicNumberUtils;
import com.xros.demo.fileserver.utils.SelfStringUtils;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ResourceUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * @Author: mzp
 * @Description: 文件服务层实现类
 * @Date: 2024/3/27 11:40
 * @Version: 1.0
 */
@Log4j2
@Service
public class FileServiceImpl extends ServiceImpl<FileMapper, FileEntity> implements IFileService {


    @Resource
    private FileMagicNumMapper fileMagicNumMapper;


    //region （描述：私有方法）


    @Transactional(rollbackFor = Exception.class)
    @Override
    public ResultVo saveFile(MultipartFile file) {

        if (file == null) {
            return ResultVo.builder()
                    .code(400)
                    .msg("上传文件不能为空");
        }
        String exceptionSt = null;
        try {


            //获取文件的md5值
            final String fileMd5 = FileUtils.getFileMd5(file.getInputStream());

            //region （描述：封装查询参数）
            LambdaQueryWrapper<FileEntity> query = new LambdaQueryWrapper<>();
            query.eq(FileEntity::getFileMd5, fileMd5);
            //endregion

            //根据文件的md5值进行查询
            FileEntity result = baseMapper.selectOne(query);

            //如果文件不为空
            if (result != null) {
                //获取文件的绝对存储路径
                String fileSavePath = getFileSavePath(result.getRelativePath());

                //获取温江的字节内容
                byte[] fileContent = result.getFileContent();

                //如果文件不存在
                if (!Files.exists(Paths.get(fileSavePath))) {
                    log.info("文件存在数据库，但不存在文件系统");
                    FileUtils.saveMultipartFileToLocal(fileContent, fileSavePath);
                }
                return ResultVo.builder().code(200).msg("文件已存在");
            }
            FileEntity saveData = getEntityByMultipartFile(file);

            int affectedRow = baseMapper.insert(saveData);

            //如果数据入库成功
            if (affectedRow > 0) {
                //获取文件保存路径
                String fileSavePath = getFileSavePath(saveData.getRelativePath());
                //保存文件到本地
                FileUtils.saveMultipartFileToLocal(file.getBytes(), fileSavePath);

            }

            return ResultVo.builder()
                    .code(affectedRow > 0 ? 200 : 500)
                    .msg(affectedRow > 0 ? "成功" : "失败");
        } catch (IOException e) {
            exceptionSt = SelfStringUtils.getExceptionStr(e);
            Log4jUtils.logMethodExp(log, this.getClass(), "saveFile", e);
        }
        return ResultVo.builder().code(500).msg("系统异常!异常消息:" + exceptionSt);

    }


    @Override
    public ResultVo<Object> saveDirectory(MultipartFile[] file) {

        if (Objects.isNull(file)) {
            return ResultVo.builder().code(400).msg("文件不能为空");
        }


        final int fileCount = file.length;
        List<FileEntity> saveList = new ArrayList<>(fileCount);

        FileEntity saveItem = null;
        for (MultipartFile multipartFile : file) {
            saveItem = getEntityByMultipartFile(multipartFile);
            saveList.add(saveItem);
        }


        int affectedRows = baseMapper.insertList(saveList);


        int saveSuccessCount = 0;
        for (FileEntity fileEntity : saveList) {
            //如果保存文件操作成功
            if (FileUtils.saveMultipartFileToLocal(fileEntity.getFileContent(), getFileSavePath(fileEntity.getRelativePath()), FileType.FILE.value().equals(fileEntity.getFileType()))) {
                saveSuccessCount++;
            }
        }

        log.info("文件总数为：{}，保存成功的文件数为：{}，保存失败的文件数为：{}", affectedRows, saveSuccessCount, file.length - saveSuccessCount);

        final boolean saveRes = affectedRows == saveSuccessCount;
        ResultVo<Object> code = ResultVo.builder().code(saveRes ? 200 : 500);

        code.setData(saveRes);;

        return code;
    }


    //endregion


    //region （描述：私有方法）


    private FileEntity getEntityByMultipartFile(MultipartFile file) {


        try {


            FileEntity saveData = new FileEntity();

            byte[] fileContent = file.getBytes();

            //获取文件的原名
            String originalFilename = file.getOriginalFilename();


            String expectedFilename = getFileNameByOriginal(originalFilename);

            //文件魔术
            saveData.setMagicNum(MagicNumberUtils.getFileHeaderHexStr(file.getInputStream()));


            //TODO(时间：2024/3/29 16:37，描述：下面的代码是有问题的，当文件没有后缀时，会被误认为是文件夹)
            //是否为文件夹
            boolean isDir = MagicNumberUtils.checkIsDirByMagicNum(saveData.getMagicNum()) && !expectedFilename.contains(".");

            final FileType fileType = isDir ? FileType.DIR : FileType.FILE;

            String fileName = null;
            String relativePath = null;
            String relativeDirPath = null;
            if (isDir) {
                originalFilename = correctFileSeparator(originalFilename);
                String errorFileName = getFileNameByOriginal(originalFilename);
                originalFilename = originalFilename.replace(File.separator + errorFileName, "");
            }
            relativePath = originalFilename;
            fileName = getFileNameByOriginal(originalFilename);
            relativeDirPath = getRelativeDirPathByOriginal(originalFilename);

            //获取文件名

            //获取文件相对路径


            //获取文件md5
            String fileMd5 = FileUtils.getFileMd5(file.getInputStream());

            //文件内容
            saveData.setFileContent(fileContent);
            //文件名
            saveData.setFileName(fileName);


            //文件类型
            saveData.setFileType(fileType.value());

            if (file.getName().contains(".")) {
                //文件后缀
                saveData.setFileSuffix(file.getName().substring(file.getName().indexOf(".")));
            } else {
                //文件后缀
                saveData.setFileSuffix("");
            }
            //文件的md5
            saveData.setFileMd5(fileMd5);
            //文件相对路径
            saveData.setRelativePath(relativePath);

            //文件夹的相对路径
            saveData.setRelativeDirPath(relativeDirPath);

            return saveData;
        } catch (IOException e) {
            Log4jUtils.logMethodExp(log, this.getClass(), "\tgetEntityByMultipartFile\t", e);
        }
        return null;

    }

    /**
     * 获取文件的保存路径
     *
     * @param originalFilename
     * @return
     */
    private String getFileSavePath(String originalFilename) {

        String path = null;
        try {
            //获取target的相对路径
            path = ResourceUtils.getURL("classpath:").getPath() + "static";
            //修正文件分隔符
            String realPath = path.replace('/', '\\').substring(1, path.length());
            //拼接文件保存的相对路径
            return realPath + File.separator + originalFilename;
        } catch (Exception e) {
            Log4jUtils.logMethodExp(log, this.getClass(), "getFileSavePath", e);
        }

        return null;


    }


    /**
     * 从 originalFilename 中获取文件的相对路径
     *
     * @param originalFilename
     * @return
     */
    private String getRelativeDirPathByOriginal(String originalFilename) {

        originalFilename = correctFileSeparator(originalFilename);

        //如果修正后文件名为空或者不包含 文件分割符
        if (originalFilename == null || !originalFilename.contains(File.separator)) {
            return "";
        }

        int index = originalFilename.lastIndexOf(File.separator);

        return originalFilename.substring(0, index);

    }


    /**
     * 从 originalFilename 中获取文件名
     *
     * @param originalFilename
     * @return
     */
    private String getFileNameByOriginal(String originalFilename) {

        //修正文件路径中的文件分隔符
        originalFilename = correctFileSeparator(originalFilename);

        //如果修正后文件名为空或者不包含 文件分割符
        if (originalFilename == null || !originalFilename.contains(File.separator)) {
            return originalFilename;
        }

        int index = originalFilename.lastIndexOf(File.separator);

        return originalFilename.substring(index + 1);

    }

    /**
     * 修正文件路径中的文件分割符号
     *
     * @param filePath
     * @return
     */
    private String correctFileSeparator(String filePath) {
        if (StringUtils.isEmpty(filePath)) {
            return null;
        }

        //修正文件分割符号
        filePath = filePath.replace("\\", File.separator);
        filePath = filePath.replace("/", File.separator);

        return filePath;
    }

    //endregion

}
